<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <script>

    /* 函数对象的call方法的作用:  执行函数, 并指定函数中的this为第一参数对象*/
    /* Function.prototype.call = function (obj, ...args) {
      console.log('--------')
      // 执行当前函数
      // this(...args)

      // 处理obj是undefined或null的情况
      if (obj===undefined || obj===null) {
        // 直接调用, 并返回执行的结果
        // return this(...args)

        // 修正obj为window
        obj = window
      }

      // 将当前函数添加为obj的临时方法
      obj.tempFn = this
      // 通过obj调用这个临时方法
      const result = obj.tempFn(...args)
      // 删除临时方法
      delete obj.tempFn
      // 返回执行的结果
      return result
    }
    */

    function apply(fn, obj, args) {
      return call(fn, obj, ...args)
    }

    function call(fn, obj, ...args) {
      // 处理obj是undefined或null的情况
      if (obj===undefined || obj===null) {
        // 直接调用, 并返回执行的结果
        // return fn(...args)

        // 修正obj为window
        obj = window
      }

      // 将当前函数添加为obj的临时方法
      obj.tempFn = fn
      // 通过obj调用这个临时方法
      const result = obj.tempFn(...args)
      // 删除临时方法
      delete obj.tempFn
      // 返回执行的结果
      return result
    }

    /* 
    bind的作用: 返回一个新函数, 新函数内部会调用原函数, 且指定this为bind的第一个参数对象
    fn2.bind(obj, 3)(1, 2)
    */
    function bind (fn, obj, ...args) {
      // 返回一个新函数
      return (...args2) => {
        // 新函数内部会调用原函数, 且指定this为bind的第一个参数对象
        return call(fn, obj, ...args, ...args2)
      }
    }
  </script>
  
  <script>
    function fn2(a, b) {
      console.log(this, a, b)
      return a + b
    }
    const obj = {m: 1}
    // console.log(fn2.call(obj, 2, 3))
    // console.log(fn2.call(undefined, 2, 3))
    // console.log(call(fn2, obj, 2, 3))
    // console.log(call(fn2, undefined, 2, 3))

    // console.log(fn2.call(obj, [2, 3]))
    // console.log(fn2.call(undefined, [2, 3]))
    // console.log(apply(fn2, obj, [2, 3]))
    // console.log(apply(fn2, undefined, [2, 3]))

    // fn2.bind(obj)(1, 2)
    // fn2.bind(obj, 3)(1, 2)
    // fn2.bind(obj, 3, 4)(1, 2) // 指定函数将来通过新函数来调用时的this和参数
    bind(fn2, obj)(1, 2)
    bind(fn2, obj, 3)(1, 2)
    bind(fn2, obj, 3, 4)(1, 2) // 指定函数将来通过新函数来调用时的this和参数

    
    /* 
    打印对象时, 会输出对象的快照(打印输出当时对象内部的数据)
    而点击时展开: 看到是此时对象的内容
    */

  </script>

  <!-- <script>
    /* 
    实例对象  ==> new函数(Function除外)产生的对象, 简称对象
    函数对象  ==> 将函数作为对象使用时, 称它为函数对象
    */
    console.dir(fn2)
    console.log(fn2.toString)//  得到的是toString方法的属性值函数
    // 方法: 方法是一个特别的属性, 属性值是一个函数

    function fn(a) {
      a(123) // 会产生什么效果呢?
    }
    fn(console.log)
    fn.call(obj) // 从语法上来说fn此时是函数对象, 从功能上来说fn是函数
  </script> -->
</body>
</html>